#pragma once
#include "../Util/util.hpp"
#include "../Util/log_util.hpp"
#include "db.hpp"
#include "online.hpp"
#include <unordered_map>
#include <memory>

#define ROWS 15
#define COLS 15
#define CHESS_WHITE 1
#define CHESS_BLACK 2
typedef enum {GAME_START, GAME_OVER} room_status;

class room
{
private:
    uint64_t _room_id;
    room_status _status;
    int _play_count;
    uint64_t _white_id;
    uint64_t _black_id;
    user_table* _ut;
    online_manager* _om;
    std::vector<std::vector<int>> _board;
private:
    bool five(int row, int col, int row_off, int col_off, int color)
    {
        int count = 1;
        int next_row = row + row_off;
        int next_col = col + col_off;
        while(next_row >= 0 && next_row < ROWS && next_col >= 0 && next_col < COLS
        && _board[next_row][next_col] == color)
        {
            count++;
            next_row += row_off;
            next_col += col_off;
        }
        next_row = row - row_off;
        next_col = col - col_off;
        while(next_row >= 0 && next_row < ROWS && next_col >= 0 && next_col < COLS
        && _board[next_row][next_col] == color)
        {
            count++;
            next_row -= row_off;
            next_col -= col_off;
        }
        return (count >= 5);
    }
    uint64_t check_win(int row, int col, int color)
    {
        if(five(row, col, 0, 1, color) || five(row, col, 1, 0, color)
        || five(row, col, -1, 1, color) || five(row, col, -1, -1, color))
        {
            return color == CHESS_WHITE ? _white_id : _black_id;
        }
        return 0;
    }
public:
    room(uint64_t room_id, user_table* ut, online_manager* om)
    :_room_id(room_id),_status(GAME_START),_play_count(0),_ut(ut),_om(om),
    _board(ROWS, std::vector<int>(COLS, 0))
    {
        LOG(INFO) << _room_id << "号房间创建成功..." << "\n";
    }
    ~room()
    {
        LOG(INFO) << _room_id << "号房间销毁成功..." << "\n";
    }
    uint64_t getId(){return _room_id;}
    room_status getStatus(){return _status;}
    int getPlayCount(){return _play_count;}
    void add_white_user(uint64_t uid)
    {
        _white_id = uid;
        _play_count++;
    }
    void add_black_user(uint64_t uid)
    {
        _black_id = uid;
        _play_count++;
    }
    uint64_t getWid(){return _white_id;}
    uint64_t getBid(){return _black_id;}

    // 处理下棋动作
    Json::Value handle_chess(Json::Value& req)
    {
        Json::Value resp = req;
        // 房间号是否与请求的匹配
        // -- //
        // 玩家是否在线，有一个掉线就是另一方赢
        uint64_t cur_uid = req["uid"].asUInt64();
        if(_om->is_in_game_room(_white_id) == false)
        {
            resp["result"] = true;
            resp["reason"] = "对方掉线，不战而胜";
            resp["winner"] = (Json::Int64)_black_id;
            return resp;
        }   
        if(_om->is_in_game_room(_black_id) == false)
        {
            resp["result"] = true;
            resp["reason"] = "对方掉线，不战而胜";
            resp["winner"] = (Json::Int64)_white_id;
            return resp;
        }   
       
        // 走棋的位置是否合理(是否被占用，没有就在此落子)
        int chess_row = req["row"].asInt();
        int chess_col = req["col"].asInt();
        if(_board[chess_row][chess_col] != 0)
        {
            resp["result"] = false;
            resp["reason"] = "该位置已经被占用";
            return resp;
        }
        int cur_color = cur_uid == _white_id ? CHESS_WHITE : CHESS_BLACK;
        _board[chess_row][chess_col] = cur_color;

        // 其中一个玩家是否已经五子连珠了（获胜）
        uint64_t winner_id = check_win(chess_row, chess_col, cur_color);
        if(winner_id != 0)
        {
            resp["reason"] = "五子连珠，获胜!";
        }
        resp["result"] = true;
        resp["winner"] = (Json::Int64)winner_id;
        return resp;
    }
    // 处理聊天动作
    Json::Value handle_chat(Json::Value& req)
    {
        Json::Value resp = req;
        // 房间号是否与请求的匹配
        // -- // 
        // 是否有敏感消息
        std::string msg = req["message"].asCString();
        auto it = msg.find("垃圾");
        if(it != std::string::npos)
        {
            resp["result"] = false;
            resp["reason"] = "含有敏感消息!";
            return resp;
        }
        // 返回消息
        resp["result"] = true;
        return resp;
    }
    // 处理玩家退出房间动作
    void handle_exit(uint64_t uid)
    {
        Json::Value resp;
        uint64_t winner_id = uid == _white_id ? _black_id : _white_id;
        // 玩家退出，另一方获胜
        if(_status == GAME_START)
        {
            resp["optype"] = "put_chess";
            resp["result"] = true;
            resp["reason"] = "对方掉线，获胜!";
            resp["room_id"] = (Json::Int64)_room_id;
            resp["uid"] = (Json::Int64)uid;
            resp["row"] = -1;
            resp["col"] = -1;
            resp["winner"] = (Json::Int64)winner_id;

            uint64_t lose_id = winner_id == _white_id ? _black_id : _white_id;
            _ut->win(winner_id);
            _ut->lose(lose_id);
            _status = GAME_OVER;
            broadcast(resp);
        }
        // 房间玩家--，广播消息
        _play_count--;
    }
    // 总的请求处理函数
    void handle_request(Json::Value& req)
    {
        Json::Value resp;
        // 房间号是否与请求的匹配
        uint64_t room_id = req["room_id"].asUInt64();
        if(_room_id != room_id)
        {
            resp["result"] = false;
            resp["reason"] = "房间号不匹配!";
            return;
        }
        // 根据不同的请求调用处理函数
        if(req["optype"].asString() == "put_chess")
        {
            resp = handle_chess(req);
            uint64_t winner_id = resp["winner"].asUInt64();
            if(winner_id != 0)
            {
                uint64_t lose_id = winner_id == _white_id ? _black_id : _white_id;
                _ut->win(winner_id);
                _ut->lose(lose_id);
                _status = GAME_OVER;
            }
        }
        else if(req["optype"].asString() == "chat")
        {
            resp = handle_chat(req);
        }
        else 
        {
            resp["optype"] = req["optype"].asString();
            resp["result"] = false;
            resp["reason"] = "未知动作";
        }
        // 广播消息
        broadcast(resp);
    }
    // 将指定信息广播给所有玩家
    void broadcast(Json::Value& req)
    {
        // 序列化
        std::string json_string;
        Json_Util::serializa(req, json_string);
        // 得到所有连接并发送
        wsserver_t::connection_ptr wconn = _om->get_conn_from_room(_white_id);
        if(wconn != nullptr)
        {
            wconn->send(json_string);
        }
        wsserver_t::connection_ptr bconn = _om->get_conn_from_room(_black_id);
        if(bconn != nullptr)
        {
            bconn->send(json_string);
        }
    }
};

typedef std::shared_ptr<room> room_ptr;
class room_manage
{
private:
    uint64_t _rid;
    std::mutex _mutex;
    user_table* _ut;
    online_manager* _om;
    std::unordered_map<uint64_t, room_ptr> _rooms;
    std::unordered_map<uint64_t, uint64_t> _users;
public:
    room_manage(user_table* ut, online_manager* om)
        :_ut(ut), _om(om),_rid(1)
    {
        LOG(INFO) << "房间管理模块创建完毕" << "\n";
    }
    ~room_manage()
    {
        LOG(INFO) << "房间管理模块销毁完毕" << "\n";
    }

    // 为两个用户创建房间， 并返回房间信息的指针对象
    room_ptr create_room(uint64_t uid1, uint64_t uid2)
    {
        // 两个用户是否在线
        if(_om->is_in_game_hall(uid1) == false)
        {
            return room_ptr();
        }
        if(_om->is_in_game_hall(uid2) == false)
        {
            return room_ptr();
        }
        // 填充房间信息
        std::unique_lock<std::mutex> lock(_mutex);// 加锁保护
        room_ptr rp(new room(_rid, _ut, _om));
        rp->add_white_user(uid1);
        rp->add_black_user(uid2);
        // 关联对应关系
        _rooms.insert({_rid, rp});
        _users.insert({uid1, _rid});
        _users.insert({uid2, _rid});
        _rid++;
        // 返回房间信息
        return rp;
    }
    // 通过房间ID获取房间信息
    room_ptr get_room_by_rid(uint64_t rid)
    {
        std::unique_lock<std::mutex> lock(_mutex);// 加锁保护
        auto it = _rooms.find(rid);
        if(it == _rooms.end())
        {
            return room_ptr();
        }
        return it->second;
    }
    // 通过用户ID获取房间信息
    room_ptr get_room_by_uid(uint64_t uid)
    {
        std::unique_lock<std::mutex> lock(_mutex);// 加锁保护
        // 先通过用户ID得到房间ID，再通过房间ID获取房间信息
        auto uit = _users.find(uid);
        if(uit == _users.end())
        {
            return room_ptr();
        }
        uint64_t rid = uit->second;
        auto rit = _rooms.find(rid);
        if(rit == _rooms.end())
        {
            return room_ptr();
        }
        return rit->second;
    }
    // 通过房间ID销毁房间
    void remove_room(uint64_t rid)
    {
        // 根据rid找到房间信息
        auto it = _rooms.find(rid);
        if(it == _rooms.end())
        {
            return;
        }
        room_ptr rp = it->second;
        uint64_t uid1 = rp->getBid();
        uint64_t uid2 = rp->getWid();
        // 销毁对应用户信息
        std::unique_lock<std::mutex> lock(_mutex);// 加锁保护
        _users.erase(uid1);
        _users.erase(uid2);
        // 销毁房间
        _rooms.erase(rid);
    }
    // 删除房间指定用户，如果房间没有用户则销毁房间
    void remove_room_user(uint64_t uid)
    {
        room_ptr rp = get_room_by_uid(uid);
        if(rp.get() == nullptr) return;
        rp->handle_exit(uid);
        if(rp->getPlayCount() == 0)
        {
            remove_room(rp->getId());
        }
    }
};